{ "cells": [ { "cell_type": "markdown", "id": "d26e3b44", "metadata": {}, "source": [ "# Decomposing Qiskit circuits" ] }, { "cell_type": "markdown", "id": "f5994d25", "metadata": {}, "source": [ "In this notebook, we show how Qiskit circuits can be converted into Perceval circuits. To do so, we take the example of a simple gate-based circuit (a circuit producing GHZ states) and we show the translation to a linear optical circuit. We also show the equivalence between the two circuits." ] }, { "cell_type": "markdown", "id": "3ed1c79e", "metadata": {}, "source": [ "As usual we start by importing the needed libraries. Note that this notebook requires the installation of Qiskit (which can be easiliy done with `pip install qiskit`)." ] }, { "cell_type": "code", "execution_count": 1, "id": "9dea723b", "metadata": {}, "outputs": [], "source": [ "import perceval as pcvl\n", "from perceval.components import catalog\n", "from perceval.converters import QiskitConverter\n", "from perceval.algorithm import Analyzer, Sampler\n", "\n", "from qiskit import QuantumCircuit\n", "from qiskit.quantum_info import Statevector" ] }, { "cell_type": "markdown", "id": "61e8fe05", "metadata": {}, "source": [ "## GHZ State generation in Qiskit" ] }, { "cell_type": "markdown", "id": "57423a74", "metadata": {}, "source": [ "We first define the circuit generating GHZ states of 3 qubits with Qiskit. To do so, we first act with a Hadamard gate on qubit 0 to put in superposition of state $|0\\rangle$ and $|1\\rangle$. Then we perform two CNOT gates using qubit 0 as control and qubits 1 and 2 as targets." ] }, { "cell_type": "code", "execution_count": 2, "id": "aef291d3", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
     ┌───┐          \n",
       "q_0: ┤ H ├──■────■──\n",
       "     └───┘┌─┴─┐  │  \n",
       "q_1: ─────┤ X ├──┼──\n",
       "          └───┘┌─┴─┐\n",
       "q_2: ──────────┤ X ├\n",
       "               └───┘
" ], "text/plain": [ " ┌───┐ \n", "q_0: ┤ H ├──■────■──\n", " └───┘┌─┴─┐ │ \n", "q_1: ─────┤ X ├──┼──\n", " └───┘┌─┴─┐\n", "q_2: ──────────┤ X ├\n", " └───┘" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Create a Quantum Circuit acting on the q register\n", "circuit = QuantumCircuit(3)\n", "\n", "# Add a H gate on qubit 0\n", "circuit.h(0)\n", "\n", "# Add CX (CNOT) gates on control qubit 0 and target qubits 1 and 2\n", "circuit.cx(0, 1)\n", "circuit.cx(0, 2)\n", "\n", "# Draw the circuit\n", "circuit.draw()" ] }, { "cell_type": "markdown", "id": "cca607ca", "metadata": {}, "source": [ "We display the final state when starting from the input state $|000\\rangle$." ] }, { "cell_type": "code", "execution_count": 3, "id": "7edaa2b3", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$$\\frac{\\sqrt{2}}{2} |000\\rangle+\\frac{\\sqrt{2}}{2} |111\\rangle$$" ], "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Set the initial state of the simulator to the ground state using from_int\n", "state = Statevector.from_int(0, 2**3)\n", "\n", "# Evolve the state by the quantum circuit\n", "state = state.evolve(circuit)\n", "\n", "#draw using latex\n", "state.draw('latex')" ] }, { "cell_type": "markdown", "id": "9a4a941f", "metadata": {}, "source": [ "## Conversion to Perceval" ] }, { "cell_type": "markdown", "id": "9d6365aa", "metadata": {}, "source": [ "With the use of `QiskitConverter`, we can transform the Qiskit circuit into a Perceval circuit. It uses 2 modes per qubit and additional modes for ancillary photons to perform deterministically two-qubit gates. Below the first six modes correspond to the three logical qubits (see the 'Spatial Modes encoding' paragraph in the 'Basics' section of the documentation) of the gate-based circuit above.\n", "\n", "The other modes are used to successfully implement two-qubit gates via heralding or post-selection. Heralding employs [4 ancillary modes](https://doi.org/10.1073/pnas.1018839108) while post-selection employs [2 ancillary modes](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.65.062324). With the option `use_postselection=True` in the method `.convert` on a `QiskitConverter` object, every CNOT but the last is implemented with a heralding scheme. Here it means that it would add $4+2$ ancillary modes. The option `use_postselection=False` only implements heralded CNOTs. Here it would mean $4+4$ ancillary modes. Note: the `use_postselection` option is `True` by default." ] }, { "cell_type": "code", "execution_count": 4, "id": "dccbd8c9", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "Φ_tl=2*piΦ_bl=piΦ_tr=pi\n", "Θ=3*pi/2\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "HERALDED CNOT\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=2.145993\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=2.145993\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=1.028622\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=1.028622\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "POSTPROCESSED CNOT\n", "\n", "\n", "\n", "\n", "\n", "Φ_tl=3*pi/2Φ_bl=piΦ_tr=pi/2\n", "Θ=1.910633\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "Φ_tl=3*pi/2Φ_bl=piΦ_tr=pi/2\n", "Θ=1.910633\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=1.910633\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "H\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "1\n", "[q0]\n", "\n", "2\n", "3\n", "[q1]\n", "\n", "4\n", "5\n", "[q2]\n", "\n", "[herald0]\n", "0\n", "\n", "[herald1]\n", "1\n", "\n", "[herald2]\n", "0\n", "\n", "[herald3]\n", "1\n", "\n", "[herald4]\n", "0\n", "\n", "[herald5]\n", "0\n", "\n", "0\n", "1\n", "[q0]\n", "\n", "2\n", "3\n", "[q1]\n", "\n", "4\n", "5\n", "[q2]\n", "\n", "[herald0]\n", "0\n", "\n", "[herald1]\n", "1\n", "\n", "[herald2]\n", "0\n", "\n", "[herald3]\n", "1\n", "\n", "[herald4]\n", "0\n", "\n", "[herald5]\n", "0\n", "" ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "qiskit_converter = QiskitConverter(catalog, backend_name=\"Naive\")\n", "quantum_processor = qiskit_converter.convert(circuit, use_postselection=True)\n", "pcvl.pdisplay(quantum_processor, recursive=True)" ] }, { "cell_type": "markdown", "id": "88ff50bd", "metadata": {}, "source": [ "With this converted circuit, we can now check that the resulting state is the same as before the conversion. By default, the input is the logical state $|000\\rangle_L$. Note that where Qiskit displays state in the order $|q_2q_1q_0\\rangle_L$, Perceval uses the reverse order $|q_0q_1q_2\\rangle_L$, but still shown as Fock states. Here, it doesn't change anything since we end with only $|000\\rangle_L$ and $|111\\rangle_L$ states." ] }, { "cell_type": "code", "execution_count": 5, "id": "8ddb1457", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
state probability
|1,0,1,0,1,0>1/2
|0,1,0,1,0,1>1/2
|1,0,0,1,1,0>0
|0,1,1,0,0,1>0
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Not necessary here\n", "quantum_processor.with_input(pcvl.LogicalState([0,0,0]))\n", "\n", "sampler = Sampler(quantum_processor)\n", "\n", "output_distribution = sampler.probs()[\"results\"]\n", "pcvl.pdisplay(output_distribution, precision=1e-2, max_v = 4)" ] }, { "cell_type": "markdown", "id": "2f14a1c5", "metadata": {}, "source": [ "This circuit can now be converted using a general interferometer decomposition so it can be implemented on a generic photonic chip." ] }, { "cell_type": "code", "execution_count": 6, "id": "d15ff2ba", "metadata": {}, "outputs": [], "source": [ "u = quantum_processor.linear_circuit().compute_unitary(use_symbolic=False)" ] }, { "cell_type": "code", "execution_count": 7, "id": "1de600cb", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=7*pi/2\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=pi\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=2.835294\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=10.655737\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=3*pi/2\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=2.711157\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=4*pi/3\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=pi/2\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=4.372552\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=pi/2\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=7*pi/2\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=1.463368\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=3.715317\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=3.509113\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=3.878573\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=1.108671\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=2.162945\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=4.370709\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=3.469026\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=4.790804\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=3.765375\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=4.213348\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=0.995599\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=pi/2\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=3.434221\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=1.246631\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=2.254402\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=0.760216\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=0.878654\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=2.262939\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=8.714223\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=pi\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi/2\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=3*pi/2\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=8.497483\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=pi/2\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=0.021139\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=0.082276\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=8.429179\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=pi/2\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3.852148\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=2.083748\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=1.057844\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=2.381377\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=0\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=1.910633\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=pi/2\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=1.823477\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=3*pi/2\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=4.913747\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=3*pi/2\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=2.453133\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=10.655737\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=2.805839\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=1.673115\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=2.081197\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=0.031518\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=1.345733\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=4.901662\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=0.74751\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=2.595615\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=1.096571\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=10.655737\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=1.861319\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=2.225344\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=3.300038\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=0.966896\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=4.090504\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=3*pi\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=0.122269\n", "\n", "\n", "\n", "\n", "\n", "\n", "Θ=7*pi/2\n", "\n", "\n", "Rx\n", "\n", "\n", "Φ=3*pi/2\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "0\n", "1\n", "2\n", "3\n", "4\n", "5\n", "6\n", "7\n", "8\n", "9\n", "10\n", "11\n", "0\n", "1\n", "2\n", "3\n", "4\n", "5\n", "6\n", "7\n", "8\n", "9\n", "10\n", "11\n", "" ], "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ub = (pcvl.Circuit(2)\n", " // pcvl.BS(theta=pcvl.Parameter(\"theta\"))\n", " // (0, pcvl.PS(phi=pcvl.Parameter(\"φ_a\"))))\n", "\n", "pc_norm = pcvl.Circuit.decomposition(u, ub, shape=\"triangle\")\n", "pcvl.pdisplay(pc_norm, compact=True, render_size=0.5)" ] }, { "cell_type": "markdown", "id": "ff0f021c", "metadata": {}, "source": [ "## A cnot based on CZ\n", "\n", "Another interesting example we can explore is how to build a cnot from a CZ gate using qiskit then convert it to Perceval. We will apply the following equivalence:\n", "\n", "![equivalence between cnot and H-CZ-H](../_static/img/cz-cnot-equivalence.png)\n", "\n", "\n", "The code in Qiskit:" ] }, { "cell_type": "code", "execution_count": 8, "id": "6516a88a", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
                  \n",
       "q_0: ──────■──────\n",
       "     ┌───┐ │ ┌───┐\n",
       "q_1: ┤ H ├─■─┤ H ├\n",
       "     └───┘   └───┘
" ], "text/plain": [ " \n", "q_0: ──────■──────\n", " ┌───┐ │ ┌───┐\n", "q_1: ┤ H ├─■─┤ H ├\n", " └───┘ └───┘" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "circuit = QuantumCircuit(2)\n", "\n", "# Add (CNOT) built using equivalence with H-CZ-H\n", "circuit.h(1)\n", "circuit.cz(0, 1)\n", "circuit.h(1)\n", "# Draw the circuit\n", "circuit.draw()" ] }, { "cell_type": "markdown", "id": "bcff0e53", "metadata": {}, "source": [ "Then we call the converter like the previous example" ] }, { "cell_type": "code", "execution_count": 9, "id": "94b97e12", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
|1,0,1,0> |1,0,0,1> |0,1,1,0> |0,1,0,1>
|1,0,1,0> 1 0 0 0
|1,0,0,1> 0 1 0 0
|0,1,1,0> 0 0 0 1
|0,1,0,1> 0 0 1 0
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "state = Statevector.from_int(0, 2**3)\n", "state = state.evolve(circuit)\n", "\n", "qiskit_converter = QiskitConverter(catalog, backend_name=\"SLOS\")\n", "quantum_processor = qiskit_converter.convert(circuit)\n", "# pcvl.pdisplay(quantum_processor, recursive=True) # the perceval processor can be displayed at this point if needed\n", "\n", "input_states = [pcvl.BasicState([1, 0, 1, 0]), pcvl.BasicState([1, 0, 0, 1]), pcvl.BasicState([0, 1, 1, 0]), pcvl.BasicState([0, 1, 0, 1])]\n", "analyzer = Analyzer(quantum_processor, input_states)\n", "pcvl.pdisplay(analyzer)" ] }, { "cell_type": "markdown", "id": "3d79ee37", "metadata": {}, "source": [ "This is the truth table of a CNOT gate\n", "\n", "## Few remarks\n", "- Controlflow operations such as measurement operator in the qiskit ciruit or `qiskit.circuit.QuantumCircuit.if_test` are not supported.\n", "- Custom gates are also not supported at the moment (see [Issue#201](https://github.com/Quandela/Perceval/issues/201))." ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }